Skip to content

feat: example-level flip gate — P→F blocks promotion regardless of gains (Closes #1446) - #1449

Merged
Lexus2016 merged 1 commit into
mainfrom
evolution/issue-1446-flip-gate
Jul 28, 2026
Merged

feat: example-level flip gate — P→F blocks promotion regardless of gains (Closes #1446)#1449
Lexus2016 merged 1 commit into
mainfrom
evolution/issue-1446-flip-gate

Conversation

@Lexus2016

Copy link
Copy Markdown
Owner

Closes #1446 — Child B of #1308, consuming the frozen probe sets Child A (#1355) shipped.

Why

Child A gave the pipeline a probe-set data layer and nothing consumed it. The promotion decision was still "did the aggregate score go up?" — the exact question AgentDevel (arXiv:2601.04620) shows ships regressions, because an aggregate can rise while specific previously-working cases break.

Their ablation is the argument: 14.8% regression rate with 4 bad releases without example-level flip gating, 3.1% with 0 with it.

What it does

Every probe lands in one of four cells:

Cell Meaning
P→F regression — it passed before, it fails now
F→P fix — the gain
P→P held
F→F still broken

Promotion blocks when P→F exceeds the threshold no matter how many F→P gains sit beside it. Gains do not buy regressions — that is the whole gate.

Three decisions worth calling out

Default max_regressions is 0. One broken previously-working example is worth a human look. Slack must be asked for explicitly.

Unrecognised grader output reads as FAIL, not pass. _passed accepts bools, status strings, and dicts with passed/pass/ok/success/status; anything else fails closed. A result that read as a pass would turn a regression into a silent P→P — the failure this gate exists to catch.

A probe present before and absent after BLOCKS by default. The probe set is frozen by contract (#1355), so a missing probe means the set was mutated or the run did not finish. Both make the comparison unsound, and the missing probe may be the one that broke. Overridable with --allow-missing.

Verified end to end

$ evolution_flip_gate.py before.json after.json
[flip-gate] BLOCK: 1 regression(s) (p2) exceed the allowed 0; 1 fix(es) do not offset breaking what previously worked
  P->F 1  F->P 1  P->P 1  F->F 0
  regressed: p2
  fixed: p3
exit=1

Exit 0 promote / 1 block / 2 bad input, so a caller can gate on it directly.

Deferred, as the issue specifies

Wiring the verdict into merge verification is Child C (#1447); version metadata on promoted skills is Child D (#1448). This slice only produces the table and the verdict.

26 tests — the coercion table, all four cells, missing-probe handling, threshold behaviour, every CLI exit path. Ruff clean.

…ains (#1446)

Child B of #1308. Child A (#1355) shipped the frozen probe-set data layer and
nothing consumed it; the promotion decision was still "did the aggregate score
go up?" — the exact question AgentDevel (arXiv:2601.04620) shows ships
regressions, since an aggregate can rise while specific previously-working cases
break. Their ablation: 14.8% regression rate with 4 bad releases without
example-level flip gating, 3.1% with 0 with it.

scripts/evolution_flip_gate.py classifies every probe into four cells —
P->F regression, F->P fix, P->P held, F->F still broken — and blocks when P->F
exceeds the threshold no matter how many F->P gains accompany it. Gains do not
buy regressions; that is the entire point of the gate.

Three decisions worth stating:

- Default max_regressions is 0. One broken previously-working example is worth
  a human look. Callers needing slack ask for it explicitly.
- Unrecognised grader output reads as FAIL, not pass. _passed accepts bools,
  status strings, and dicts with passed/pass/ok/success/status; anything else
  fails closed, because a result that reads as a pass turns a regression into
  a silent P->P.
- A probe present before and absent after BLOCKS by default. The probe set is
  frozen by contract (#1355), so a missing probe means either the set was
  mutated or the run did not finish — both make the comparison unsound, and the
  missing probe may be the one that broke. Overridable with --allow-missing.

CLI exits 0 promote / 1 block / 2 bad input, so a caller can gate on it
directly. Verified end to end, not just via the unit tests.

Deferred as the issue specifies: wiring the verdict into merge verification is
Child C (#1447), version metadata is Child D (#1448).

26 tests covering the coercion table, all four cells, missing-probe handling,
threshold behaviour and every CLI exit path; ruff clean.

Closes #1446
@github-actions github-actions Bot added the bug Something isn't working label Jul 28, 2026
@Lexus2016
Lexus2016 merged commit 6c865f6 into main Jul 28, 2026
37 checks passed
@Lexus2016
Lexus2016 deleted the evolution/issue-1446-flip-gate branch July 28, 2026 10:38
Lexus2016 added a commit that referenced this pull request Jul 28, 2026
Child C of #1308. The flip-gate engine (#1446, PR #1449) classifies
per-probe P→F regressions but nothing consumed the verdict — the merge
gate would still self-merge a PR that broke previously-working probes.

This wires the verdict into check_merge_policy_with_quality():
- check_flip_gate() runs evolution_flip_gate.compare_runs/flip_verdict
  and returns FLIP_GATE_BLOCK violations naming the regressed probes
- A block stops the autonomous self-merge
- --flip-gate-override REASON downgrades to a recorded warning (auditable)
- Skills without probe sets are unaffected (opt-in per skill)
- Fail-closed: missing probe data or unavailable module blocks the merge

Success criteria met:
✓ P→F regression above threshold prevents the merge
✓ Blocking comment names specific probes that flipped
✓ Override is possible and leaves a recorded reason
✓ Skill without probe set merges as before

Closes #1447

Co-authored-by: Hermes Evolution <evolution@hermes.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[INCREMENT] Flip-gate engine: per-example P→F / F→P table + block verdict (Child B of #1308)

1 participant